﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using FileTransferProtocol;
using Fcdbas.Core.Net;
using Fcdbas.Core.Util;
using Fcdbas.Core.Xml;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace FileTransferClient
{
    public partial class frmUpdate : Form
    {
       // private List<TFileInfo> FileWaitList = new List<TFileInfo>();
        private List<TFileInfo> FileDownLoadList = new List<TFileInfo>();
       // private List<TransferHead> TSendHeadList = new List<TransferHead>();
        private List<TransferHead> TDownLoadHeadList = new List<TransferHead>();
      
        private Dictionary<string, SocketClient> FDownLoadClientList = new Dictionary<string, SocketClient>();
        private ProxyInfo proxyInfo = null;
        private object LockIsDL = new object();
        private object LockFinished = new object();
        private Config config;
        private string DownLoadPathTemp;
        public event OnEventDelegate FEvent;
        private string FileSendConfig;
        private string FileDownLoadConfig;
       
        private SocketClientSync client;
        private bool IsManual = true;
        public frmUpdate(bool isManual)
        {
            InitializeComponent();
            IsManual = isManual;
            
        }

       

       
       // private int BlockCount = 5;
        private int SendSize = 8192;
       
        private SocketClientSync CreateSocketClient(string host, int port)
        {
            SocketClientSync client = new SocketClientSync(new IPEndPoint(IPAddress.Parse(host), port));
            client.DelimiterType = DelimiterType.dtMessageTailExcludeOnReceive;
            client.Delimiter = Encoding.GetEncoding(1252).GetBytes("%$#@^&");
            client.EncryptType = EncryptType.etNone;
            client.CompressionType = CompressionType.ctNone;
            client.MessageBufferSize = SendSize * 2;
            client.SocketBufferSize = 4096;
            if (proxyInfo != null)
                client.ProxyInfo = proxyInfo;
            return client;
        }
        class Session
        {
            public SocketClientSync Client
            {
                get;
                set;
            }
            public FileBlock Block
            {
                get;
                set;
            }
            public NetSession NetSession
            {
                get;
                set;
            }
        }
        
      
       
        delegate void Delegate_Progress(FileTransferClient task);
        void fclient_BlockFinished(object sender, BlockFinishedEventArgs e)
        {
            
        }
        private void ShowDownLoadStatus(FileTransferClient task)
        {
            TFileInfo fileInfo = FileDownLoadList.Find(p => p.MD5 == task.TransferFile.FileInfo.MD5);
            if (fileInfo == null)
                return;
            fileInfo.ComplatedSize = task.ComplatedSize;
            fileInfo.KByteAverSpeed = task.ByteAverSpeed;
            fileInfo.TimePast = task.TimePast.ToString("c");
            fileInfo.TimeRemaining = task.TimeRemaining.ToString("c");
            bsDownLoad.DataSource = FileDownLoadList;
            bsDownLoad.ResetBindings(false);
            long totalSize = 0;
            long cpSize = 0;
            for (int i = 0; i < FileDownLoadList.Count; i++)
            {
                totalSize += FileDownLoadList[i].FileSize;
                cpSize += FileDownLoadList[i].ComplatedSize;
            }
            if (fileInfo.KByteAverSpeed < 1)
                return;
            long remain = totalSize - cpSize;
            long remaintime = (remain / 1024) / ((long)fileInfo.KByteAverSpeed * 3) ;
            string msg = "";
            if (remaintime > 120)
                msg = (remaintime / 60).ToString() + " 分";
            else
                msg = remaintime.ToString() + " 秒";
            lbStatus.Text = string.Format(statusMsg, msg);
        }
        delegate void DelegateFinished(string fileName);
        
        void FileDownLoadFinished(string fileName)
        {
            try
            {
                if (string.IsNullOrEmpty(fileName))
                    return;
                FileDownLoadClient ft = null;
                if (FDownLoadClientList.Keys.Contains(fileName))
                {
                    SocketClient client = FDownLoadClientList[fileName];
                    if (client == null)
                        return;
                    ft = client.SocketService as FileDownLoadClient;
                }

                try
                {
                    lock (LockIsDL)
                    {
                        TFileInfo fi = FileDownLoadList.Find(p => p.IsDownLoading == false);
                        if (fi != null)
                            DownLoadFile(fi, ft);
                    }
                }
                catch (Exception ex)
                {
                    Fcdbas.Core.Log.XTrace.WriteException(ex);
                }
               
                //client.Stop();
                TransferHead head = TDownLoadHeadList.Find(p => (p.FileInfo.MD5) == fileName);
                TDownLoadHeadList.Remove(head);
                FDownLoadClientList.Remove(fileName);
                TFileInfo fileInfo = FileDownLoadList.Find(p => p.MD5 == fileName);
                if (fileInfo != null)
                {
                    lock (LockFinished)
                    {
                        fileInfo.IsFinished = true;
                        fileInfo.ComplatedSize = fileInfo.FileSize;
                    }
                    //FileDownLoadList.Remove(fileInfo);
                    bsDownLoad.ResetBindings(false);
                   // gvDownLoad.Refresh();
                }
                this.progressBar1.Value = this.progressBar1.Value + 1;
                StartApp();
            }
            catch (Exception ex)
            {
                Fcdbas.Core.Log.XTrace.WriteException(ex, FileDownLoadClient.LogPath);
                Console.WriteLine(ex.Message);
            }
        }

        private void StartApp()
        {
            TFileInfo fi = null;
            lock (LockFinished)
            {
                fi = FileDownLoadList.Find(p => p.IsFinished == false);
            }
            if (FileDownLoadList.Count == 0 || fi == null)
            {
                Application.Exit();
            //    if (IsManual)
                {
                    string app = Fcdbas.Core.Configuration.Config.GetConfig<string>("App", "main.exe");
                    Process proc = Process.Start(app,"true");
                }
            }
        }
        private string configPath;
        private void FormClient_Load(object sender, EventArgs e)
        {
            configPath = AppDomain.CurrentDomain.BaseDirectory + "Config.xml";
            
            config = Fcdbas.Core.Xml.XmlHelper.ToXmlFileEntity<Config>(configPath);
            if (config == null)
                config = new Config();
           
            config.DownLoadPath = AppDomain.CurrentDomain.BaseDirectory;
            if (config.ProxyUse != ProxyUse.NoUse)
                proxyInfo = Config.CreateProxy(config);
            DownLoadPathTemp = config.DownLoadPath + "\\Temp";
            FileSendConfig = AppDomain.CurrentDomain.BaseDirectory + "SendClient.xml";
            FileDownLoadConfig = AppDomain.CurrentDomain.BaseDirectory + "DownLoadClient.xml";
           
       //     TDownLoadHeadList = Fcdbas.Core.Xml.XmlHelper.ToXmlFileEntity<List<TransferHead>>(FileDownLoadConfig);
            if (TDownLoadHeadList == null)
                TDownLoadHeadList = new List<TransferHead>();
            if (!GetUpdateFileList())
                return;
            bool update = Fcdbas.Core.Configuration.Config.GetConfig<bool>("AutoUpdate", false);
            if(update)
                StartDownLoad(3);
            StartApp();
        }

        private void FormClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (SocketClient client in FDownLoadClientList.Values)
            {
                FileDownLoadClient ft = client.SocketService as FileDownLoadClient;
                if (ft != null)
                    ft.Stop = true;
                Thread.Sleep(1000);
                //  client.Stop();
            }
            // Thread.Sleep(1000);
            for (int i = 0; i < TDownLoadHeadList.Count; i++)
            {
                for (int j = 0; j < TDownLoadHeadList[i].Blocks.Count; j++)
                {
                    TDownLoadHeadList[i].Blocks[j].Data = null;
                    if (TDownLoadHeadList[i].Blocks[j].FStream != null)
                    {
                        //TDownLoadHeadList[i].Blocks[j].FStream.Flush();
                        TDownLoadHeadList[i].Blocks[j].FStream.Close();
                        TDownLoadHeadList[i].Blocks[j].FStream = null;
                    }
                    
                }
            }
      //      Fcdbas.Core.Xml.XmlHelper.ToXmlFile(TDownLoadHeadList, FileDownLoadConfig);
            Fcdbas.Core.Xml.XmlHelper.ToXmlFile(config, configPath);
            //if (client.Connected)
            //    client.Disconnect();
        }

        private void StartDownLoad(int downloadCount)
        {
            if (FileDownLoadList == null || FileDownLoadList.Count == 0)
                return;
            int nCount = 0;
            int fCount = FileDownLoadList.Count;
            for (int i = 0; i < fCount; i++)
            {
                if (nCount >= downloadCount)
                    break;
                TFileInfo fi = FileDownLoadList[i];
                DownLoadFile(fi,null);
                //FileWaitList.Remove(fi);
                nCount++;
            }
            //bsSendFile.DataSource = FileWaitList;
            //bsSendFile.ResetBindings(false);
            //gvFileList.Refresh();
        }
        private byte[] ReadFromSocket(SocketClientSync client)
        {
            byte[] buffer = client.ReadBytes(3000);
            int tryCount = 0;
            while (buffer == null && tryCount < 3)
            {
                Thread.Sleep(1000);
                buffer = client.ReadBytes(3000);
                tryCount++;
            }
            return buffer;
        }
        private List<TFileInfo> CompareLocal(List<TFileInfo> fileList)
        {
            List<TFileInfo> list = new List<TFileInfo>();
            foreach (TFileInfo fi in fileList)
            {
                string file = config.DownLoadPath + "\\" + fi.FileName;
                if (!File.Exists(file))
                    list.Add(fi);
                else
                {
                    if (fi.MD5 != Fcdbas.Core.Security.DataHelper.HashFile(file))
                        list.Add(fi);
                }
            }
            return list;
        }
        private string statusMsg = "";
        private bool GetUpdateFileList()
        {
            if (client == null)
            {
                client = CreateSocketClient(this.config.IP, this.config.Port);
                client.Connect();
            }
            if (!client.Connected)
                client.Connect();
            if (client.Connected)
            {
                NetSession session = new NetSession(FileTransferProtocol.ConnectionState.Connected);
                session.CmdType = FileTransferProtocol.CommandType.FetchList;
                session.State = FileTransferProtocol.ConnectionState.Connected;
                byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(session);
                client.Write(buffer);

                buffer = ReadFromSocket(client);

                if (buffer == null)
                    return false;
                NetSession sRes = ObjectHelper.DeserialzieObject<NetSession>(buffer);
                if (!string.IsNullOrEmpty(sRes.Content))
                {
                    List<TFileInfo> fileList = sRes.Content.ToXmlEntity<List<TFileInfo>>();
                    if (fileList == null)
                    {
                        MessageBox.Show("获取不到下载的文件或者可提供下载的文件为0");
                        return false;
                    }
                    FileDownLoadList = CompareLocal(fileList);
                    bsDownLoad.DataSource = FileDownLoadList;
                    lbStatus.Text = string.Format(lbStatus.Text, FileDownLoadList.Count,"{0}");
                    statusMsg = lbStatus.Text;
                    this.progressBar1.Maximum = FileDownLoadList.Count;
                    this.progressBar1.Value = 0;
                }
                else
                {
                    MessageBox.Show("获取不到下载的文件或者可提供下载的文件为0");
                }
            }
            else
            {
                MessageBox.Show("连接不到服务器，请联系系统管理员！");
                return false;
            }
            return true;
        }
        private void btnDownLoadFile_Click(object sender, EventArgs e)
        {
            StartDownLoad(3);
        }
        private void DownLoadFile(TFileInfo file ,FileDownLoadClient dlClient)
        {
            NetSession sRes =new NetSession (FileTransferProtocol.ConnectionState.Connected);
            sRes.CmdType = FileTransferProtocol.CommandType.FetchDownFileInfo;
            sRes.Content = file.File;
            byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(sRes);
            client.Write(buffer);

            buffer = ReadFromSocket(client);
            if (buffer == null)
            {
                MessageBox.Show("获取文件信息失败,请重试！");
                return;
            }
            sRes = ObjectHelper.DeserialzieObject<NetSession>(buffer);
            if (sRes == null)
            {
                MessageBox.Show("数据异常，请联系支持人员");
                return;
            }
            if (sRes.CmdType == FileTransferProtocol.CommandType.DownInit)
            {
                bool resume = false;
                TransferHead head= InitDownLoadHead(sRes.Head, ref resume);
                if (resume)
                {
                    for (int i = 0; i < head.Blocks.Count; i++)
                    {
                        head.Blocks[i].StartProcess = false;
                    }
                }
                if (dlClient == null)
                {
                    FileDownLoadClient DownLoadClient = new FileDownLoadClient(FEvent, head);
                    DownLoadClient.AllFinished += new AllFinishedEventHandler(DownLoadClient_AllFinished);
                    DownLoadClient.BlockFinished += new BlockFinishedEventHandler(DownLoadClient_BlockFinished);
                    DownLoadClient.TransferSum += new TransferEventHandler(DownLoadClient_TransferSum);
                    SocketClient socketClient = new SocketClient(CallbackThreadType.ctIOThread, DownLoadClient, DelimiterType.dtMessageTailExcludeOnReceive, Encoding.GetEncoding(1252).GetBytes("%$#@^&"), 1024 * 10, 1024 * 160);
                    for (int i = 0; i < sRes.Head.Blocks.Count + 2; i++)
                    {
                        socketClient.AddConnector(String.Empty, new IPEndPoint(IPAddress.Parse(this.config.IP), config.Port), proxyInfo, EncryptType.etNone, CompressionType.ctNone, null, 10, 2000);
                    }
                    socketClient.Start();

                    FDownLoadClientList.Add(sRes.Head.FileInfo.MD5, socketClient);
                }
                else
                {
                    dlClient.StartDownLoad(head);
                }
                lock (LockIsDL)
                {
                    file.IsDownLoading = true;
                }
                //file.MD5 = sRes.Head.FileInfo.MD5;
                //FileDownLoadList.Add(sRes.Head.FileInfo);
                //bsDownLoad.DataSource = FileDownLoadList;
            }
        }
        private TransferHead InitDownLoadHead(TransferHead tHead, ref bool resume)
        {
            TransferHead th = TDownLoadHeadList.Find(p => p.FileInfo.FileName == tHead.FileInfo.FileName && p.FileInfo.FileSize == tHead.FileInfo.FileSize);
            if (th != null)
            {
                resume = true;
                return th;
            }
            tHead.FileInfo.FilePath = config.DownLoadPath ;
            if (!Directory.Exists(tHead.FileInfo.FilePath))
            {
                Directory.CreateDirectory(tHead.FileInfo.FilePath);
            }
            string tempath = DownLoadPathTemp + "\\" + tHead.Guid;
            if (!Directory.Exists(tempath))
            {
                Directory.CreateDirectory(tempath);
            }
            for (int i = 0; i < tHead.Blocks.Count; i++)
            {
                FileBlock block = tHead.Blocks[i];
                block.TempFile = tempath + "\\" + block.TempFile;
            }

            TDownLoadHeadList.Add(tHead);
            resume = false;
            return tHead;
        }
        void DownLoadClient_TransferSum(object sender, EventArgs e)
        {
            FileDownLoadClient Client = sender as FileDownLoadClient;
            if (Client == null)
                return;
            if (InvokeRequired)
            {

                this.Invoke(new Delegate_Progress(ShowDownLoadStatus), Client);
            }
            else
            {

                ShowDownLoadStatus(Client);
            }
        }

        void DownLoadClient_BlockFinished(object sender, BlockFinishedEventArgs e)
        {
            throw new NotImplementedException();
        }

        void DownLoadClient_AllFinished(object sender, AllFinishedEventArgs e)
        {
            if (InvokeRequired)
            {

                this.Invoke(new DelegateFinished(FileDownLoadFinished), e.FileName);
            }
            else
            {

                FileDownLoadFinished(e.FileName);
            }
        }

        

        private void btnProxy_Click(object sender, EventArgs e)
        {
            dlgProxySetting dlg = new dlgProxySetting(config);
            if(dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.proxyInfo = dlg.ProxyInfo;
                client = CreateSocketClient(this.config.IP, this.config.Port);
                GetUpdateFileList();
            }
        }

        private void gvDownLoad_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            e.Graphics.DrawString((e.RowIndex + 1).ToString(),
                e.InheritedRowStyle.Font,
                new SolidBrush(Color.DarkBlue),
                e.RowBounds.Location.X + 15,
                e.RowBounds.Location.Y + 5);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            StartApp();
        }

       
    }
}
